Allow to separate GtkTextMark creation from buffer insertion. (#132818,
authorMatthias Clasen <mclasen@redhat.com>
Sat, 26 May 2007 04:10:42 +0000 (04:10 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Sat, 26 May 2007 04:10:42 +0000 (04:10 +0000)
2007-05-25  Matthias Clasen  <mclasen@redhat.com>

        Allow to separate GtkTextMark creation from buffer insertion.
        (#132818, Gustavo Giráldez, patch by Yevgen Muntyan)

        * gtk/gtktextmarkprivate.h:
        * gtk/gtktextmark.[hc] (gtk_text_mark_new): New function to
        create a GtkTextMark.

        * gtk/gtktextbuffer.[hc] (gtk_text_buffer_add_mark): New
        function to add an existing mark to a buffer.

        * gtk/gtktextbtree.c: Allow adding existing marks.

        * gtk/gtk.symbols: Add new functions.

        * tests/testtextbuffer.c: Add some tests for new mark
        functionality.

svn path=/trunk/; revision=17922

ChangeLog
docs/reference/ChangeLog
docs/reference/gtk/gtk-sections.txt
gtk/gtk.symbols
gtk/gtktextbtree.c
gtk/gtktextbuffer.c
gtk/gtktextbuffer.h
gtk/gtktextmark.c
gtk/gtktextmark.h
gtk/gtktextmarkprivate.h
tests/testtextbuffer.c

index 9aeb1335dce2391ab1bf4f430604d32c9ae14503..ece998228f8bdb2789eb12e145e343c6324a8bcd 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2007-05-25  Matthias Clasen  <mclasen@redhat.com>
+
+       Allow to separate GtkTextMark creation from buffer insertion.
+       (#132818, Gustavo Giráldez, patch by Yevgen Muntyan)
+
+       * gtk/gtktextmarkprivate.h:
+       * gtk/gtktextmark.[hc] (gtk_text_mark_new): New function to 
+       create a GtkTextMark.
+
+       * gtk/gtktextbuffer.[hc] (gtk_text_buffer_add_mark): New 
+       function to add an existing mark to a buffer. 
+
+       * gtk/gtktextbtree.c: Allow adding existing marks.
+
+       * gtk/gtk.symbols: Add new functions.
+
+       * tests/testtextbuffer.c: Add some tests for new mark 
+       functionality.
+
 2007-05-25  Xan Lopez  <xan@gnome.org>
 
        * gtk/gtkaction.c: (gtk_action_set_short_label):
index cbec4c0164266659af7c68a1d9b0e3dcaadeb85a..8a94a29a6c10bfc8bf34954678e70d1fa44be208 100644 (file)
@@ -1,3 +1,7 @@
+2007-05-25   Matthias Clasen  <mclasen@redhat.com>
+
+       * gtk/gtk-sections.txt: Add new functions
+
 2007-05-24   Matthias Clasen  <mclasen@redhat.com>
 
        * === Released 2.11.0 ===
index 37fbeea6d358f546b5f790e5c061cccc832c9f0b..f4059825b9bae2cdc0acd531bc7024fb6bed2ae7 100644 (file)
@@ -3411,6 +3411,7 @@ gtk_text_buffer_create_child_anchor
 gtk_text_buffer_create_mark
 gtk_text_buffer_move_mark
 gtk_text_buffer_move_mark_by_name
+gtk_text_buffer_add_mark
 gtk_text_buffer_delete_mark
 gtk_text_buffer_delete_mark_by_name
 gtk_text_buffer_get_mark
@@ -3597,6 +3598,7 @@ gtk_text_iter_get_type
 <FILE>gtktextmark</FILE>
 <TITLE>GtkTextMark</TITLE>
 GtkTextMark
+gtk_text_mark_new
 gtk_text_mark_set_visible
 gtk_text_mark_get_visible
 gtk_text_mark_get_deleted
index 9f7e02fe15c988f32f46d5b8fb928db412d4882a..5b28f131856efe19235177d7e1a2ffdcb28c6e01 100644 (file)
@@ -3552,6 +3552,7 @@ gtk_text_buffer_backspace
 gtk_text_buffer_begin_user_action
 gtk_text_buffer_copy_clipboard
 gtk_text_buffer_create_child_anchor
+gtk_text_buffer_add_mark
 gtk_text_buffer_create_mark
 gtk_text_buffer_create_tag 
 gtk_text_buffer_cut_clipboard
@@ -3789,6 +3790,7 @@ gtk_text_line_segment_split
 
 #if IN_HEADER(__GTK_TEXT_MARK_H__)
 #if IN_FILE(__GTK_TEXT_MARK_C__)
+gtk_text_mark_new
 gtk_text_mark_get_buffer
 gtk_text_mark_get_deleted
 gtk_text_mark_get_left_gravity
index 7601803371e23cd6c17d2eacba11eab7eaa17c5a..93f6b4b3d98fe0d36411a9ce1173716f87fe7652 100644 (file)
@@ -2692,7 +2692,12 @@ real_set_mark (GtkTextBTree      *tree,
   g_return_val_if_fail (_gtk_text_iter_get_btree (where) == tree, NULL);
 
   if (existing_mark)
-    mark = existing_mark->segment;
+    {
+      if (gtk_text_mark_get_buffer (existing_mark) != NULL)
+       mark = existing_mark->segment;
+      else
+       mark = NULL;
+    }
   else if (name != NULL)
     mark = g_hash_table_lookup (tree->mark_table,
                                 name);
@@ -2752,9 +2757,13 @@ real_set_mark (GtkTextBTree      *tree,
     }
   else
     {
-      mark = _gtk_mark_segment_new (tree,
-                                    left_gravity,
-                                    name);
+      if (existing_mark)
+       g_object_ref (existing_mark);
+      else
+       existing_mark = gtk_text_mark_new (name, left_gravity);
+
+      mark = existing_mark->segment;
+      _gtk_mark_segment_set_tree (mark, tree);
 
       mark->body.mark.line = _gtk_text_iter_get_text_line (&iter);
 
@@ -2980,7 +2989,8 @@ gtk_text_mark_set_visible (GtkTextMark       *mark,
     {
       seg->body.mark.visible = setting;
 
-      redisplay_mark (seg);
+      if (seg->body.mark.tree)
+       redisplay_mark (seg);
     }
 }
 
index 20e34be08ba37b51248a8122e653f19682b9066a..68364b60b17e42b1e837016c88d5f6ec89a661eb 100644 (file)
@@ -2079,6 +2079,44 @@ gtk_text_buffer_create_mark (GtkTextBuffer *buffer,
                                    left_gravity, FALSE);
 }
 
+/**
+ * gtk_text_buffer_add_mark:
+ * @buffer: a #GtkTextBuffer
+ * @mark: the mark to add
+ * @where: location to place mark
+ *
+ * Adds the mark at position @where. The mark must not be added to
+ * another buffer, and if its name is not %NULL then there must not
+ * be another mark in the buffer with the same name.
+ *
+ * Emits the "mark_set" signal as notification of the mark's initial
+ * placement.
+ *
+ * Since: 2.12
+ **/
+void
+gtk_text_buffer_add_mark (GtkTextBuffer     *buffer,
+                          GtkTextMark       *mark,
+                          const GtkTextIter *where)
+{
+  const gchar *name;
+
+  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+  g_return_if_fail (GTK_IS_TEXT_MARK (mark));
+  g_return_if_fail (where != NULL);
+  g_return_if_fail (gtk_text_mark_get_buffer (mark) == NULL);
+
+  name = gtk_text_mark_get_name (mark);
+
+  if (name != NULL && gtk_text_buffer_get_mark (buffer, name) != NULL)
+    {
+      g_critical ("Mark %s already exists in the buffer", name);
+      return;
+    }
+
+  gtk_text_buffer_set_mark (buffer, mark, NULL, where, FALSE, FALSE);
+}
+
 /**
  * gtk_text_buffer_move_mark:
  * @buffer: a #GtkTextBuffer
@@ -2129,13 +2167,13 @@ gtk_text_buffer_get_iter_at_mark (GtkTextBuffer *buffer,
  *
  * Deletes @mark, so that it's no longer located anywhere in the
  * buffer. Removes the reference the buffer holds to the mark, so if
- * you haven't called g_object_ref () on the mark, it will be freed. Even
+ * you haven't called g_object_ref() on the mark, it will be freed. Even
  * if the mark isn't freed, most operations on @mark become
- * invalid. There is no way to undelete a
- * mark. gtk_text_mark_get_deleted () will return TRUE after this
- * function has been called on a mark; gtk_text_mark_get_deleted ()
- * indicates that a mark no longer belongs to a buffer. The "mark_deleted"
- * signal will be emitted as notification after the mark is deleted.
+ * invalid, until it gets added to a buffer again with 
+ * gtk_text_buffer_add_mark(). Use gtk_text_mark_get_deleted() to  
+ * find out if a mark has been removed from its buffer.
+ * The "mark_deleted" signal will be emitted as notification after 
+ * the mark is deleted.
  **/
 void
 gtk_text_buffer_delete_mark (GtkTextBuffer *buffer,
index 715b514acf1831a608a12353e92ecb8c40709be5..9a0eb9d28e4886cc5550c7b0f16b47896878e990 100644 (file)
@@ -247,6 +247,9 @@ GtkTextChildAnchor *gtk_text_buffer_create_child_anchor (GtkTextBuffer *buffer,
                                                          GtkTextIter   *iter);
 
 /* Mark manipulation */
+void           gtk_text_buffer_add_mark    (GtkTextBuffer     *buffer,
+                                            GtkTextMark       *mark,
+                                            const GtkTextIter *where);
 GtkTextMark   *gtk_text_buffer_create_mark (GtkTextBuffer     *buffer,
                                             const gchar       *mark_name,
                                             const GtkTextIter *where,
index 281538e3f948b8db1574e55e4911ca3504c0e11d..febec6b4b35a80c9e8502b665fa32e65551b5cd9 100644 (file)
 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
 #include <config.h>
 #include "gtktextbtree.h"
+#include "gtkprivate.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
 
-static void gtk_text_mark_finalize (GObject *obj);
+static void gtk_text_mark_set_property (GObject         *object,
+                                       guint            prop_id,
+                                       const GValue    *value,
+                                       GParamSpec      *pspec);
+static void gtk_text_mark_get_property (GObject         *object,
+                                       guint            prop_id,
+                                       GValue          *value,
+                                       GParamSpec      *pspec);
+static void gtk_text_mark_finalize     (GObject         *object);
+
+static GtkTextLineSegment *gtk_mark_segment_new (GtkTextMark *mark_obj);
 
 G_DEFINE_TYPE (GtkTextMark, gtk_text_mark, G_TYPE_OBJECT)
 
-static void
-gtk_text_mark_init (GtkTextMark *mark)
-{
-  mark->segment = NULL;
-}
+enum {
+  PROP_0,
+  PROP_NAME,
+  PROP_LEFT_GRAVITY
+};
 
 static void
 gtk_text_mark_class_init (GtkTextMarkClass *klass)
@@ -69,6 +80,30 @@ gtk_text_mark_class_init (GtkTextMarkClass *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   object_class->finalize = gtk_text_mark_finalize;
+  object_class->set_property = gtk_text_mark_set_property;
+  object_class->get_property = gtk_text_mark_get_property;
+
+  g_object_class_install_property (object_class,
+                                   PROP_NAME,
+                                   g_param_spec_string ("name",
+                                                        P_("Name"),
+                                                        P_("Mark name"),
+                                                        NULL,
+                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  g_object_class_install_property (object_class,
+                                   PROP_LEFT_GRAVITY,
+                                   g_param_spec_boolean ("left-gravity",
+                                                         P_("Left gravity"),
+                                                         P_("Whether the mark has left gravity"),
+                                                         FALSE,
+                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gtk_text_mark_init (GtkTextMark *mark)
+{
+  mark->segment = gtk_mark_segment_new (mark);
 }
 
 static void
@@ -98,6 +133,88 @@ gtk_text_mark_finalize (GObject *obj)
   G_OBJECT_CLASS (gtk_text_mark_parent_class)->finalize (obj);
 }
 
+static void
+gtk_text_mark_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+  gchar *tmp;
+  GtkTextMark *mark = GTK_TEXT_MARK (object);
+  GtkTextLineSegment *seg = mark->segment;
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      tmp = seg->body.mark.name;
+      seg->body.mark.name = g_value_dup_string (value);
+      g_free (tmp);
+      break;
+
+    case PROP_LEFT_GRAVITY:
+      if (g_value_get_boolean (value))
+       seg->type = &gtk_text_left_mark_type;
+      else
+       seg->type = &gtk_text_right_mark_type;
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_text_mark_get_property (GObject    *object,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+  GtkTextMark *mark = GTK_TEXT_MARK (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, gtk_text_mark_get_name (mark));
+      break;
+
+    case PROP_LEFT_GRAVITY:
+      g_value_set_boolean (value, gtk_text_mark_get_left_gravity (mark));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+/**
+ * gtk_text_mark_new:
+ * @name: mark name or %NULL
+ * @left_gravity: whether the mark should have left gravity
+ *
+ * Creates a text mark. Add it to a buffer using gtk_text_buffer_add_mark().
+ * If @name is %NULL, the mark is anonymous; otherwise, the mark can be 
+ * retrieved by name using gtk_text_buffer_get_mark(). If a mark has left 
+ * gravity, and text is inserted at the mark's current location, the mark 
+ * will be moved to the left of the newly-inserted text. If the mark has 
+ * right gravity (@left_gravity = %FALSE), the mark will end up on the 
+ * right of newly-inserted text. The standard left-to-right cursor is a 
+ * mark with right gravity (when you type, the cursor stays on the right
+ * side of the text you're typing).
+ *
+ * Return value: new #GtkTextMark
+ *
+ * Since: 2.12
+ **/
+GtkTextMark *
+gtk_text_mark_new (const gchar *name,
+                  gboolean     left_gravity)
+{
+  return g_object_new (GTK_TYPE_TEXT_MARK,
+                      "name", name,
+                      "left-gravity", left_gravity,
+                      NULL);
+}
+
 /**
  * gtk_text_mark_get_visible:
  * @mark: a #GtkTextMark
@@ -140,8 +257,8 @@ gtk_text_mark_get_name (GtkTextMark *mark)
  * @mark: a #GtkTextMark
  * 
  * Returns %TRUE if the mark has been removed from its buffer
- * with gtk_text_buffer_delete_mark(). Marks can't be used
- * once deleted.
+ * with gtk_text_buffer_delete_mark(). See gtk_text_buffer_add_mark()
+ * for a way to add it to a buffer again.
  * 
  * Return value: whether the mark is deleted
  **/
@@ -212,28 +329,22 @@ gtk_text_mark_get_left_gravity (GtkTextMark *mark)
         + sizeof (GtkTextMarkBody)))
 
 
-GtkTextLineSegment*
-_gtk_mark_segment_new (GtkTextBTree *tree,
-                       gboolean      left_gravity,
-                       const gchar  *name)
+static GtkTextLineSegment *
+gtk_mark_segment_new (GtkTextMark *mark_obj)
 {
   GtkTextLineSegment *mark;
 
   mark = (GtkTextLineSegment *) g_malloc0 (MSEG_SIZE);
-  mark->body.mark.name = g_strdup (name);
-
-  if (left_gravity)
-    mark->type = &gtk_text_left_mark_type;
-  else
-    mark->type = &gtk_text_right_mark_type;
+  mark->body.mark.name = NULL;
+  mark->type = &gtk_text_right_mark_type;
 
   mark->byte_count = 0;
   mark->char_count = 0;
 
-  mark->body.mark.obj = g_object_new (GTK_TYPE_TEXT_MARK, NULL);
-  mark->body.mark.obj->segment = mark;
+  mark->body.mark.obj = mark_obj;
+  mark_obj->segment = mark;
 
-  mark->body.mark.tree = tree;
+  mark->body.mark.tree = NULL;
   mark->body.mark.line = NULL;
   mark->next = NULL;
 
@@ -243,6 +354,23 @@ _gtk_mark_segment_new (GtkTextBTree *tree,
   return mark;
 }
 
+void
+_gtk_mark_segment_set_tree (GtkTextLineSegment *mark,
+                           GtkTextBTree       *tree)
+{
+  g_assert (mark->body.mark.tree == NULL);
+  g_assert (mark->body.mark.obj != NULL);
+
+  mark->byte_count = 0;
+  mark->char_count = 0;
+
+  mark->body.mark.tree = tree;
+  mark->body.mark.line = NULL;
+  mark->next = NULL;
+
+  mark->body.mark.not_deleteable = FALSE;
+}
+
 static int                 mark_segment_delete_func  (GtkTextLineSegment *segPtr,
                                                       GtkTextLine        *line,
                                                       int                 treeGone);
index 851123c4ac0bcae4fc50c0d98baf839278ae7e4d..41bcb9492f2565536699b3ebf56e1806abf007bf 100644 (file)
@@ -88,6 +88,8 @@ void           gtk_text_mark_set_visible (GtkTextMark *mark,
                                           gboolean     setting);
 gboolean       gtk_text_mark_get_visible (GtkTextMark *mark);
 
+GtkTextMark          *gtk_text_mark_new              (const gchar *name,
+                                                     gboolean     left_gravity);
 G_CONST_RETURN gchar* gtk_text_mark_get_name         (GtkTextMark *mark);
 gboolean              gtk_text_mark_get_deleted      (GtkTextMark *mark);
 GtkTextBuffer*        gtk_text_mark_get_buffer       (GtkTextMark *mark);
index 43e979736689a086c7e85aeef9bf42de29a8a392..d2ff38137172008b01a358d048e32a59bfbfa7d6 100644 (file)
@@ -49,9 +49,8 @@ struct _GtkTextMarkBody {
   guint not_deleteable : 1;
 };
 
-GtkTextLineSegment *_gtk_mark_segment_new   (GtkTextBTree       *tree,
-                                             gboolean            left_gravity,
-                                             const gchar        *name);
+void _gtk_mark_segment_set_tree (GtkTextLineSegment *mark,
+                                GtkTextBTree       *tree);
 
 G_END_DECLS
 
index b739deadcc322dfb75f4c6fcf4cd23a10468851c..eaae8b7352ef4807d23e2f248341dc008ae49f9d 100644 (file)
@@ -48,6 +48,8 @@ static void line_separator_tests (void);
 
 static void logical_motion_tests (void);
 
+static void mark_tests (void);
+
 int
 main (int argc, char** argv)
 {
@@ -75,6 +77,9 @@ main (int argc, char** argv)
   /* Create a buffer */
   buffer = gtk_text_buffer_new (NULL);
 
+  /* Marks */
+  mark_tests ();
+
   /* Check that buffer starts with one empty line and zero chars */
 
   n = gtk_text_buffer_get_line_count (buffer);
@@ -1210,3 +1215,73 @@ logical_motion_tests (void)
 
   g_object_unref (buffer);
 }
+
+static void
+mark_tests (void)
+{
+  GtkTextBuffer *buf1, *buf2;
+  GtkTextMark *mark;
+  GtkTextIter iter;
+
+  buf1 = gtk_text_buffer_new (NULL);
+  buf2 = gtk_text_buffer_new (NULL);
+
+  gtk_text_buffer_get_start_iter (buf1, &iter);
+  mark = gtk_text_buffer_create_mark (buf1, "foo", &iter, TRUE);
+  g_object_ref (mark);
+  gtk_text_mark_set_visible (mark, TRUE);
+  gtk_text_buffer_delete_mark (buf1, mark);
+
+  g_assert (gtk_text_mark_get_visible (mark));
+  g_assert (gtk_text_mark_get_left_gravity (mark));
+  g_assert (!strcmp ("foo", gtk_text_mark_get_name (mark)));
+  g_assert (gtk_text_mark_get_buffer (mark) == NULL);
+  g_assert (gtk_text_mark_get_deleted (mark));
+  g_assert (gtk_text_buffer_get_mark (buf1, "foo") == NULL);
+
+  gtk_text_buffer_get_start_iter (buf2, &iter);
+  gtk_text_buffer_add_mark (buf2, mark, &iter);
+  gtk_text_buffer_insert (buf2, &iter, "ewfwefwefwe", -1);
+  gtk_text_buffer_get_iter_at_mark (buf2, &iter, mark);
+
+  g_assert (gtk_text_mark_get_visible (mark));
+  g_assert (gtk_text_iter_is_start (&iter));
+  g_assert (gtk_text_mark_get_left_gravity (mark));
+  g_assert (!strcmp ("foo", gtk_text_mark_get_name (mark)));
+  g_assert (gtk_text_mark_get_buffer (mark) == buf2);
+  g_assert (!gtk_text_mark_get_deleted (mark));
+  g_assert (gtk_text_buffer_get_mark (buf2, "foo") == mark);
+
+  gtk_text_buffer_delete_mark (buf2, mark);
+  gtk_text_mark_set_visible (mark, FALSE);
+  g_object_unref (mark);
+
+  mark = gtk_text_mark_new ("blah", TRUE);
+  gtk_text_buffer_get_start_iter (buf1, &iter);
+  gtk_text_mark_set_visible (mark, TRUE);
+  gtk_text_buffer_add_mark (buf1, mark, &iter);
+
+  g_assert (gtk_text_mark_get_visible (mark));
+  g_assert (gtk_text_mark_get_buffer (mark) == buf1);
+  g_assert (!gtk_text_mark_get_deleted (mark));
+  g_assert (gtk_text_buffer_get_mark (buf1, "blah") == mark);
+  g_assert (!strcmp ("blah", gtk_text_mark_get_name (mark)));
+
+  gtk_text_mark_set_visible (mark, FALSE);
+  gtk_text_buffer_delete_mark (buf1, mark);
+  g_assert (!gtk_text_mark_get_visible (mark));
+  g_assert (gtk_text_buffer_get_mark (buf1, "blah") == NULL);
+  g_assert (gtk_text_mark_get_buffer (mark) == NULL);
+  g_assert (gtk_text_mark_get_deleted (mark));
+
+  gtk_text_buffer_get_start_iter (buf2, &iter);
+  gtk_text_buffer_add_mark (buf2, mark, &iter);
+  g_assert (gtk_text_mark_get_buffer (mark) == buf2);
+  g_assert (!gtk_text_mark_get_deleted (mark));
+  g_assert (gtk_text_buffer_get_mark (buf2, "blah") == mark);
+  g_assert (!strcmp ("blah", gtk_text_mark_get_name (mark)));
+
+  g_object_unref (mark);
+  g_object_unref (buf1);
+  g_object_unref (buf2);
+}